home *** CD-ROM | disk | FTP | other *** search
- qArticle 312 of comp.sys.acorn:
- Xref: rusmv1 comp.sys.acorn:312 eunet.micro.acorn:133
- Path: rusmv1!ira.uka.de!fauern!unido!mcsun!ukc!acorn!john
- From: john@acorn.co.uk (John Bowler)
- Newsgroups: comp.sys.acorn,eunet.micro.acorn
- Subject: Re: Any got a malloc function with debugging facilities?
- Summary: No, but this may help
- Keywords: malloc
- Message-ID: <4938@acorn.co.uk>
- Date: 1 Feb 91 20:14:56 GMT
- References: <3430@gos.ukc.ac.uk>
- Organization: Acorn Computers Ltd, Cambridge, UK
- Lines: 130
-
- In article <3430@gos.ukc.ac.uk> nms@ukc.ac.uk (N.M.Smith) writes:
- >Has anyone got a malloc functions that has debugging features? The problem
- >I have is that there is a large piece of C code that does _a lot_ of mallocing
- >of complex structures. The memory usage is so high that it doesn't run a 440.
- >I know that a least some of the structures are freed after use but I need to
- >know if there is any other memory that the structure pointed to that is not
- >freed.
-
- Debugging malloc's probably won't help very much unless RISC OS specific.
- You need to know what store is in use *and* who allocated it (although
- brave users of debuggers have been seen attempting to deduce who allocated
- heap space by exmaining the contents :-)). Finding out what store is
- in use and who allocated it is frequently useful anyway, even if the
- programs storage use isn't apparently excessive. The RISC OS malloc
- makes it impossible to even find out what store is in use - you can work
- out which regions of memory are in use, but not (necessarily) where individual
- allocations within those regions start or end.
-
- The approach I have used in the past (on RISC iX, but the problems are
- similar) involves code along the following lines (it is obviously RISCOS
- specific...) No guarantees this code is correct, I write it each time
- I need it.
-
- #include <stdlib.h>
-
- #define RND(x) (((x)+sizeof (int)-1)/sizeof (int))
- #define CHECK1 0x12345678 /* Or anything you like */
- #define CHECK2 0x9abcdef0 /* Or anything you like */
-
- static int *list;
-
- void *MALLOC(size_t size) {
- int *ptr = malloc(size+20);
-
- if (ptr == NULL) return ptr;
- ptr[0] = CHECK1;
- ptr[1] = caller_pc(); /* Identifies caller of MALLOC */
- ptr[2] = size;
- ptr[3] = (int)list; /* SIC */
- list = ptr;
- ptr[1+RND(size)] = CHECK2;
- return ptr+4;
- }
-
- /* Make the following as complicated as you like */
- static void check(int *pi) {
- if (pi[0] != CHECK1 || pi[1+RND(pi[2])] != CHECK2)
- /* Store overwritten */;
- }
-
- static int *remove(int *pi) {
- int **lp = &list;
- while (*lp) {
- /* Checks every allocated item on each free() */
- check(*lp);
- if (*lp == pi) {
- *lp = (int *)pi[3];
- /* pi now removed from list */
- return pi;
- }
- /* Point to next list pointer */
- lp = (int **)&((*lp)[3]);
- }
- /* *lp == NULL, pi not in list */
- /* ERROR - invalid pointer to free */
- return NULL;
- }
-
- void FREE(void *ptr) {
- int *p = remove(ptr);
-
- if (p) free(p-4);
- }
-
- The implementation of REALLOC should be fairly obvious. This
- code is proof against bogus pointers being passed to free()
- (in particular, it is proof against multiple freeing). Also each
- storage unit contains the result of the caller_pc() function;
- if this function returns the pc of the caller of malloc (and similarly
- for realloc) it is possible to use these and the list static variable
- to find the pc of each call to malloc which is not matched by a
- free.
-
- To ensure the code gets called you need to convert every malloc
- call to a MALLOC call, and so on. Do this using #define's in the
- code or command line options:-
-
- -Dmalloc=MALLOC -Dfree=FREE -Drealloc=REALLOC
-
- The only problem is that this does *not* get the malloc calls in
- RISCOS_Lib - I can see no easy way of catching these, as RISCOS_Lib
- has already been compiled. (You can do it in RISC iX 1.2 by renaming
- the symbols and using a non-shared libc library.)
-
- To write caller_pc you need to resort to objasm and the APCS
- documentation. The simplest implementation is:-
-
- |_caller_pc| LDR A1, [FP, #-4]
- MOVS PC, LR
-
- Then the result is the address of the instruction immediately
- after the (original) call to MALLOC (etc). Actually the following
- bit of C will do the same (possibly only at the revision and release
- of the compiler which I have on RISC iX).
-
- static int dummy(void) { return 22; }
-
- void *caller_pc(void) {
- int a = dummy();
- int *fp = (int *)((&a)[1]); /* Revolting */
- return (void *)fp[-1];
- }
-
- Sometimes on RISC OS you will get an address in MALLOC itself,
- because of stack overflow, caller_pc() could be made to work round this,
- but it hardly seems worth it.
-
- The only problem with this approach is that the addition of 20 bytes
- to each allocation distorts heap use enormously, however this should
- not cause problems when trying to track down failures to free things.
- There are a lot of potential approaches to detecting free failures.
- One is to simply find out which malloc caller allocated the greatest
- number of outstanding storage units. Another is to note a position
- in the list using a debugging function (make a call to MALLOC and
- read the value of the ``list'' static) then, some time later, examine
- the list down to this point in another function - that will avoid
- the large numbers of (effectively) static allocations which occur
- during program startup.
-
- John Bowler (jbowler@acorn.co.uk)
-
-
-